package com.weituotian.system.shiro;

import com.weituotian.system.dao.IResourceDao;
import com.weituotian.system.dao.IRoleDao;
import com.weituotian.system.dao.IUserDao;
import com.weituotian.system.entity.User;
import com.weituotian.system.entity.enums.OpenState;
import com.weituotian.system.entity.enums.UserType;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Set;

/**
 * 自定义的指定Shiro验证用户登录的类
 * 注入的应该是dao http://www.jjoe64.com/2014/01/apache-shiro-with-hibernatesql-full.html
 * Created by Administrator on 2016-10-10.
 */
public class ShiroDbRealm extends AuthorizingRealm {

    private static Logger LOGGER = LoggerFactory.getLogger(ShiroDbRealm.class);

    private final TransactionTemplate transactionTemplate;

    private final IUserDao userDao;

    private final IRoleDao roleDao;

    private final IResourceDao resourceDao;

    private final AbstractSessionDAO sessionDAO;

    @Autowired
    public ShiroDbRealm(IRoleDao roleDao, IUserDao userDao, IResourceDao resourceDao, AbstractSessionDAO sessionDAO, PlatformTransactionManager transactionManager) {
        this.roleDao = roleDao;
        this.userDao = userDao;
        this.resourceDao = resourceDao;
        this.sessionDAO = sessionDAO;

        transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.setReadOnly(true);
    }



    /**
     * 验证当前登录的Subject
     * 经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        return transactionTemplate.execute(new TransactionCallback<AuthenticationInfo>() {
            public AuthenticationInfo doInTransaction(TransactionStatus status) {
                return doGetAuthenticationInfo(authcToken, status);
            }
        });
    }

    private AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken, TransactionStatus status)
            throws AuthenticationException {
        LOGGER.info("Shiro开始登录认证");

        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        User user = userDao.loginFindBy(token.getUsername());

        // 账号不存在
        if (user == null) {
            return null;
        }

        // 账号未启用
        if (user.getStatus() != OpenState.OPEN) {
            throw new DisabledAccountException("帐号已经禁止登录！");
        }

        //使用自定义的shirouser,没必要
        //List<Integer> roleList = roleService.findRoleIdListByUserId(user.getId());
        //ShiroUser shiroUser = new ShiroUser(user.getId(), user.getLoginname(), user.getName(), roleList);

        // 认证缓存信息
        return new SimpleAuthenticationInfo(user, user.getPassword().toCharArray(), getName());
    }

    /**
     * 为当前登录的Subject授予角色和权限
     * 该方法的调用时机为需授权资源被访问时
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return transactionTemplate.execute(new TransactionCallback<AuthorizationInfo>() {
            public AuthorizationInfo doInTransaction(TransactionStatus status) {
                return doGetAuthorizationInfo(principals, status);
            }
        });
    }

    private AuthorizationInfo doGetAuthorizationInfo(
            final PrincipalCollection principals, TransactionStatus status) {
        User user = (User) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //添加角色
        Set<String> roleSet;
        //添加资源
        Set<String> urlSet;

        if (user.getUserType() == UserType.TOPADMIN) {
            //超级管理员特殊处理,总是获得所有角色和资源
            info.addRole("superAdmin");
            LOGGER.debug("用户{}被授予超级管理员", user);
            roleSet = roleDao.getRoleNameSet(roleDao.findAll());
            urlSet = resourceDao.getResourcesUrlSet(resourceDao.findAll());
        } else {
            //根据用户id获得相应的角色和权限
            roleSet = roleDao.getRoleNameSet(roleDao.findRolesByUserId(user.getId()));
            urlSet = resourceDao.getResourcesUrlSet(resourceDao.findResourcesByUserId(user.getId()));
        }

        info.setStringPermissions(roleSet);
        info.setStringPermissions(urlSet);

        LOGGER.debug("用户{}被授予的角色{}", user, roleSet);
        LOGGER.debug("用户{}被授予的资源url{}", user, urlSet);

        return info;
    }


}
